from django_redis import get_redis_connection
from rest_framework import serializers
from goods.models import SKU
from .models import OrderInfo, OrderGoods
from django.utils import timezone
from decimal import Decimal
from django.db import transaction


class CartSKUSerializer(serializers.ModelSerializer):
    """订单中商品"""
    count = serializers.IntegerField(label='商品购买数量')

    class Meta:
        model = SKU
        fields = ['id', 'name', 'default_image_url', 'price', 'count']


class OrderSettlementSerializer(serializers.Serializer):
    """去结算"""

    freight = serializers.DecimalField(label='运费', max_digits=10, decimal_places=2)
    skus = CartSKUSerializer(many=True)


class CommitOrderSerializer(serializers.ModelSerializer):
    """保存订单"""

    class Meta:
        model = OrderInfo
        fields = ['order_id', 'address', 'pay_method']
        read_only_fields = ['order_id']  # 指定此字段只输出
        # 指定地址和支付方式字段只做输入(反序列化)
        extra_kwargs = {
            'address': {
                'write_only': True
            },
            'pay_method': {
                'write_only': True
            }
        }

    def create(self, validated_data):
        """重写此方法做四张表的操作"""

        # 获取当前保存订单时需要的信息
        # 获取当前的用户对象
        user = self.context['request'].user
        # 生成order_id: 当前时间 + user.id   from django.utils import timezone  20190311113550000000001
        order_id = timezone.now().strftime('%Y%m%d%H%M%S') + '%09d' % user.id
        # 获取校验后收货地址
        address = validated_data.get('address')
        # 获取支付方式
        pay_method = validated_data.get('pay_method')
        # 如果当前用户选择的是支付宝支付 订单状态应该为待支付 反之待发货
        status = (OrderInfo.ORDER_STATUS_ENUM['UNPAID']
                  if pay_method == OrderInfo.PAY_METHODS_ENUM['ALIPAY']
                  else OrderInfo.ORDER_STATUS_ENUM['UNSEND'])

        #手动开启事务
        with transaction.atomic():

            #创建一个保存点
            save_point = transaction.savepoint()
            try:
                # 保存订单基本信息 OrderInfo（一）
                order = OrderInfo.objects.create(
                    order_id=order_id,
                    user=user,
                    address=address,
                    total_count=0,  # 后期有了商品 总数量和总价再进行计算
                    total_amount=Decimal('0.00'),
                    freight=Decimal('10.00'),
                    pay_method=pay_method,
                    status=status
                )

                # 从redis读取购物车中被勾选的商品信息
                redis_conn = get_redis_connection('cart')
                # 获取购物车 hash和set集合数据
                cart_dict_redis = redis_conn.hgetall('cart_%s' % user.id)
                selected_ids = redis_conn.smembers('selected_%s' % user.id)
                # 定义一个字典将勾选的商品及count取出保存到新字典中
                cart_dict = {}
                for sku_id_bytes in selected_ids:
                    cart_dict[int(sku_id_bytes)] = int(cart_dict_redis[sku_id_bytes])

                # 在这个地方不要一下全部取出sku模型,因为后期在资源抢夺时会为缓存问题
                # skus = SKU.objects.filter(id__in=cart_dict.keys())
                for sku_id in cart_dict:
                    # 对一个商品可以无限次下单,只到成功或库存不足
                    while True:
                        # 获取sku对象
                        sku = SKU.objects.get(id=sku_id)
                        # 获取出count的值
                        buy_count = cart_dict[sku_id]

                        # 获取sku商品原本的库存和销量
                        origin_stock = sku.stock
                        origin_sales = sku.sales

                        # 判断库存
                        if origin_stock < buy_count:
                            raise serializers.ValidationError('库存不足')

                        # 计算sku的新库存和销量
                        new_stock = origin_stock - buy_count
                        new_sales = origin_sales + buy_count

                        # 减少库存，增加销量 sku
                        # sku.stock -= buy_count  # 减少库存
                        # sku.sales += buy_count
                        # sku.stock = new_stock
                        # sku.sales = new_sales
                        # sku.save()
                        """
                        这里使用的乐观锁：当程序每次执行进入到了for循环里面，比如查出的sku_id为1 的商品库存数量为4，
                        此时修改了库存的数量为3，并且保存到了数据库，当程序执行的时候，把前面查出来的sku_id 为1 的商品
                        库存数量为4，再去数据库查，如果相同，表示没人修改，可以更新库存，否则表示别人抢过资源，不再执行库存更新
                        """
                        result = SKU.objects.filter(stock=origin_stock, sales=origin_sales, id=sku_id).update(
                            stock=new_stock, sales=new_sales)

                        # 如果条件成功说明有资源抢夺问题,要加在修改保存数据之前
                        if result == 0:
                            continue
                        # 修改spu销量
                        spu = sku.goods
                        spu.sales += buy_count
                        spu.save()

                        # 保存订单商品信息 OrderGoods（多）
                        orderGood = OrderGoods.objects.create(
                            order=order,
                            sku=sku,
                            count=buy_count,
                            price = sku.price

                         )
                        # 累加计算总数量和总价  16: 2, 1: 2
                        order.total_count += order.freight
                        order.total_amount += (sku.price * buy_count)

                        break  # 如果当前商品下单成功 退出死循环进行对下一个商品下单

                # 最后加入邮费和保存订单信息
                order.total_amount += order.freight
                order.save()
            except Exception:
             # 数据回滚
                transaction.savepoint_rollback(save_point)
                raise serializers.ValidationError('库存不足')
            else:
                # 提交保存点
                transaction.savepoint_commit(save_point)
                # 清除购物车中已结算的商品

        pl = redis_conn.pipeline()
        pl.hdel('cart_%d' % user.id, *selected_ids)
        pl.srem('selected_%s' % user.id, *selected_ids)
        pl.execute()

        return order
